package com.jerry.usbserial.util.task.usb

import android.content.Context
import com.jerry.usbserial.activity.UsbService
import com.jerry.usbserial.util.*
import java.io.File
import java.io.FileInputStream
import kotlin.concurrent.thread
import kotlin.math.ceil

class UsbFirmwareTask(context: Context, usbService: UsbService, private val firmwareFile: File, private val imageFile: File) :
    UsbTask(context, usbService) {

    private var mFirmwareFileLength: Long = firmwareFile.length()
    private var mFirmwareSlice: Int
    private var mFirmwareCrc = byteArrayOf()

    private var mImageFileLength: Long = imageFile.length()
    private var mImageSlice: Int
    private var mImageCrc = byteArrayOf()

    init {
        this.mFirmwareSlice = ceil(mFirmwareFileLength / FIRMWARE_SLICE_SIZE.toFloat()).toInt()
        this.mImageSlice = ceil(mImageFileLength / IMAGE_SLICE_SIZE.toFloat()).toInt()
    }

    override fun interruptTask(): Boolean = mStep != STEP_CONNECT_TEST_5

    override fun start() {
        connectTest1()
    }

    private fun connectTest1() {
        mStep = STEP_CONNECT_TEST_1
        postMsg("连接测试1...")
        write(CMD_CONNECTION_TEST_1.hexTo64ByteArray())
    }

    private fun connectTest2() {
        mStep = STEP_CONNECT_TEST_2
        postMsg("连接测试2...")
        write(CMD_CONNECTION_TEST_2.hexTo64ByteArray())
    }

    private fun firmwareCheckCode() {
        mStep = STEP_FIRMWARE_CHECK_CODE
        //固件升级: 发送验证码
        postMsg("发送验证码...")
        getCheckCode(FIRMWARE_CHECK_CODES, false).forEach { write(it, false) }

        //固件升级: 擦除
        postMsg("擦除历史...")
        write("4C".hexTo64ByteArray(), false)

        //固件升级: 发送升级内容
        firmwareFileSend()
    }

    private fun firmwareFileSend() {
        mStep = STEP_FIRMWARE_FILE_SEND
        postMsg("小固件升级中...")
        thread {
            val fis = FileInputStream(firmwareFile)
            for (i in 0 until mFirmwareSlice) {
                val waitSendSize: Int =
                    if (i == mFirmwareSlice - 1) (mFirmwareFileLength - i * FIRMWARE_SLICE_SIZE).toInt() else FIRMWARE_SLICE_SIZE

                var sendComplete = false
                var offset = 0
                while (!sendComplete) {
                    val cmdList = mutableListOf<Byte>()
                    when {
                        offset == 0 -> {
                            cmdList.addAll("4A30".hexToByteArray().toList())
                            cmdList.addAll((i * FIRMWARE_SLICE_SIZE).to4LByteArray().toList())
                            cmdList.addAll(waitSendSize.to2LByteArray().toList())
                            val dataArray = ByteArray(if (waitSendSize < 56) waitSendSize else 56)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)

                            offset += dataArray.size
                        }
                        waitSendSize - offset > 62 -> {
                            cmdList.addAll("4A31".hexToByteArray().toList())
                            val dataArray = ByteArray(62)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)

                            offset += dataArray.size
                        }
                        else -> {
                            cmdList.addAll("4A32".hexToByteArray().toList())
                            val dataArray = ByteArray(waitSendSize - offset)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)
                            offset += dataArray.size
                            sendComplete = true
                        }
                    }
                    Thread.sleep(10)
                }

                val sendLength = i * FIRMWARE_SLICE_SIZE + waitSendSize
                val process = (sendLength * 100L / mFirmwareFileLength).toInt()
                postProcess(process)
            }
            fis.close()
            firmwareCrcSend()
        }
    }

    private fun firmwareCrcSend() {
        //固件升级: 文件crc
        mStep = STEP_FIRMWARE_CRC_SEND
        postMsg("固件校验中...")

        val cmdList = mutableListOf<Byte>()
        cmdList.addAll("4E000000".hexToByteArray().toList())
        cmdList.addAll(mFirmwareFileLength.to4LByteArray().toList())
        val fileArray = readIO(FileInputStream(firmwareFile))
        mFirmwareCrc = CRCUtil.getCrc32(fileArray)
        cmdList.addAll(mFirmwareCrc.toList())
        write(cmdList.to64ByteArray())
    }

    private fun connectTest3() {
        mStep = STEP_CONNECT_TEST_3
        Thread.sleep(1000)
        postMsg("连接测试3...")

        write(CMD_CONNECTION_TEST_3.hexTo64ByteArray())
    }

    private fun connectTest4() {
        mStep = STEP_CONNECT_TEST_4
        postMsg("连接测试4...")

        write(CMD_CONNECTION_TEST_4.hexTo64ByteArray())
    }

    private fun imageCheckCode() {
        mStep = STEP_IMAGE_CHECK_CODE
        //镜像升级: 发送验证码
        postMsg("发送验证码...")
        getCheckCode(IMAGE_CHECK_CODES, true).forEach { write(it, false) }
        //镜像升级: 复位
        postMsg("复位中...")
        write("4F000000000080".hexTo64ByteArray(), false)

        connectTest5()
    }

    private fun connectTest5() {
        mStep = STEP_CONNECT_TEST_5
        Thread.sleep(2000)
        postMsg("连接测试5...")
        write(CMD_CONNECTION_TEST_5.hexTo64ByteArray())
    }

    private fun connectTest6() {
        mStep = STEP_CONNECT_TEST_6
        postMsg("连接测试6...")

        write(CMD_CONNECTION_TEST_6.hexTo64ByteArray())
    }

    private fun imageQueryMM1() {
        //镜像升级: 第一次查询存储器型号
        mStep = STEP_IMAGE_QUERY_MM_1
        postMsg("查询寄存器1...")

        write(CMD_QUERY_MM_1.hexTo64ByteArray())
    }

    private fun imageQueryMM2() {
        //镜像升级: 第二次查询存储器型号
        mStep = STEP_IMAGE_QUERY_MM_2
        postMsg("查询寄存器2...")

        write(CMD_QUERY_MM_2.hexTo64ByteArray())
    }

    private fun imageFileSend() {
        mStep = STEP_IMAGE_FILE_SEND
        postMsg("镜像升级中...")

        thread {
            val fis = FileInputStream(imageFile)
            for (i in 0 until mImageSlice) {
                val waitSendSize: Int = if (i == mImageSlice - 1) (mImageFileLength - i * IMAGE_SLICE_SIZE).toInt() else IMAGE_SLICE_SIZE
                var sendComplete = false
                var offset = 0
                while (!sendComplete) {
                    val cmdList = mutableListOf<Byte>()
                    when {
                        offset == 0 -> {
                            cmdList.addAll("6A".hexToByteArray().toList())
                            cmdList.addAll((i * IMAGE_SLICE_SIZE).to4LByteArray().toList())
                            cmdList.add((i % 2).toByte())
                            cmdList.add(57.toByte())
                            val dataArray = ByteArray(if (waitSendSize < 57) waitSendSize else 57)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)
                            offset += dataArray.size
                        }
                        waitSendSize - offset > 62 -> {
                            cmdList.addAll("6B".hexToByteArray().toList())
                            cmdList.add(62.toByte())
                            val dataArray = ByteArray(62)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)
                            offset += dataArray.size
                        }
                        else -> {
                            cmdList.addAll("6C".hexToByteArray().toList())
                            cmdList.add((waitSendSize - offset).toByte())
                            val dataArray = ByteArray(waitSendSize - offset)
                            fis.read(dataArray)
                            cmdList.addAll(dataArray.toList())
                            write(cmdList.to64ByteArray(), false)
                            sendComplete = true
                        }
                    }
                }
                val sendLength = i * IMAGE_SLICE_SIZE + waitSendSize
                val process = (sendLength * 100L / mImageFileLength).toInt()
                postProcess(process)
                Thread.sleep(1)
            }
            fis.close()

            imageCrcSend()
        }
    }

    private fun imageCrcSend() {
        mStep = STEP_IMAGE_CRC_SEND
        Thread.sleep(1000)
        postMsg("镜像校验中...")

        val cmdList = mutableListOf<Byte>()
        cmdList.addAll("6E00000000".hexToByteArray().toList())
        cmdList.addAll(mImageFileLength.to4LByteArray().toList())
        val fileArray = readIO(FileInputStream(imageFile))
        mImageCrc = CRCUtil.getCrc32(fileArray)
        cmdList.addAll(mImageCrc.toList())
        write(cmdList.to64ByteArray())
    }

    private fun restart() {
        mStep = STEP_RESTART

        write(CMD_CONNECTION_TEST_1.hexTo64ByteArray(), false)
        write(CMD_CONNECTION_TEST_2.hexTo64ByteArray(), false)
        write(CMD_RESTART.hexTo64ByteArray(), false)

        postCompleted("校验完成,升级成功!")
    }

    private fun parseResult(data: ByteArray, length: Int): ByteArray {
        val byteArray = ByteArray(length)
        if (data.isNotEmpty() && data.size >= length)
            System.arraycopy(data, 0, byteArray, 0, length)
        return byteArray
    }


    override fun onResult(data: ByteArray) {
        super.onResult(data)
        when (mStep) {
            STEP_CONNECT_TEST_1 -> {
                val result = CMD_CONNECTION_TEST_1_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试1...通过")
                    connectTest2()
                } else {
                    postMsg("连接测试1...重试")
                    connectTest1()
                }
            }
            STEP_CONNECT_TEST_2 -> {
                val result = CMD_CONNECTION_TEST_2_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试2...通过")
                    firmwareCheckCode()
                } else {
                    postMsg("连接测试2...重试")
                    connectTest2()
                }
            }
            STEP_FIRMWARE_CRC_SEND -> {
                val byteArray = ByteArray(4)
                if (data.isNotEmpty() && data.size > byteArray.size)
                    System.arraycopy(data, 1, byteArray, 0, byteArray.size)
                if (byteArray.contentEquals(mFirmwareCrc)) {
                    postMsg("固件校验...通过")
                    connectTest3()
                } else {
                    postMsg("固件校验...重试")
                    firmwareCrcSend()
                }
            }
            STEP_CONNECT_TEST_3 -> {
                val result = CMD_CONNECTION_TEST_3_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试3...通过")
                    connectTest4()
                } else {
                    postMsg("连接测试3...重试")
                    connectTest3()
                }
            }
            STEP_CONNECT_TEST_4 -> {
                val result = CMD_CONNECTION_TEST_4_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试4...通过")
                    imageCheckCode()
                } else {
                    postMsg("连接测试4...重试")
                    connectTest4()
                }
            }

            STEP_CONNECT_TEST_5 -> {
                val result = CMD_CONNECTION_TEST_5_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试5...通过")
                    connectTest6()
                } else {
                    postMsg("连接测试5...重试")
                    connectTest5()
                }
            }
            STEP_CONNECT_TEST_6 -> {
                val result = CMD_CONNECTION_TEST_6_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("连接测试6...通过")
                    imageQueryMM1()
                } else {
                    postMsg("连接测试6...重试")
                    connectTest6()
                }
            }
            STEP_IMAGE_QUERY_MM_1 -> {
                imageQueryMM2()
            }
            STEP_IMAGE_QUERY_MM_2 -> {
                val result = CMD_QUERY_MM_2_RESULT.hexToByteArray()
                val byteArray = parseResult(data, result.size)
                if (byteArray.contentEquals(result)) {
                    postMsg("寄存器查询...通过")
                    imageFileSend()
                } else {
                    postMsg("寄存器查询...重试")
                    imageQueryMM2()
                }
            }
            STEP_IMAGE_CRC_SEND -> {
                val start = parseResult(data, 2)
                if (start.contentEquals("7E30".hexToByteArray())) {
                    val byteArray = ByteArray(4)
                    System.arraycopy(data, 2, byteArray, 0, byteArray.size)
                    byteArray.reverse()
                    val value = java.lang.Long.parseLong(byteArray.toHex(), 16)
                    val process = (value * 100 / mImageFileLength).toInt()
                    postProcess(process)
                }
                if (start.contentEquals("7E31".hexToByteArray())) {
                    postProcess(100)
                    val byteArray = ByteArray(4)
                    System.arraycopy(data, 2, byteArray, 0, byteArray.size)
                    if (byteArray.contentEquals(mImageCrc)) {
                        restart()
                    } else {
                        postMsg("镜像校验...重试")
                        imageCrcSend()
                    }
                }
            }
        }
    }


    private fun getCheckCode(checkCodes: LongArray, isImage: Boolean): MutableList<ByteArray> {
        val codes = mutableListOf<Byte>()
        checkCodes.forEach {
            codes.addAll(it.to4LByteArray().toList())
        }
        val cmdList = mutableListOf<ByteArray>()
        val blockSize = 60
        val block = ceil(codes.size / 60.0).toInt()
        for (i in 0 until block) {
            val cmd = mutableListOf<Byte>()
            val waitSendSize: Int = if (i == block - 1) codes.size - i * blockSize else blockSize
            cmd.addAll("49".hexToByteArray().toList())
            cmd.add((waitSendSize / 4).toByte())
            val starAddr = if (isImage) 0x0000 else 0x1F00
            val addrArray = (starAddr + if (i == 0) 0 else (i * blockSize)).to2LByteArray()
            cmd.addAll(addrArray.toList())
            val dataArray = codes.subList(i * blockSize, i * blockSize + waitSendSize).toByteArray()
            cmd.addAll(dataArray.toList())
            cmdList.add(cmd.to64ByteArray())
        }
        return cmdList
    }

    companion object {
        private const val FIRMWARE_SLICE_SIZE = 2048
        private const val IMAGE_SLICE_SIZE = 512

        private const val CMD_CONNECTION_TEST_1 = "4B304C543A417200DECDE495"
        private const val CMD_CONNECTION_TEST_1_RESULT = "4B802AED16DECDE49501008876"
        private const val CMD_CONNECTION_TEST_2 = "4B314C545A497200C2FC6EB7"
        private const val CMD_CONNECTION_TEST_2_RESULT = "4B257C873DC2FC6EB701008876"
        private const val CMD_CONNECTION_TEST_3 = "4B304C543A417200DECDE495"
        private const val CMD_CONNECTION_TEST_3_RESULT = "4B802AED16DECDE49501008876"
        private const val CMD_CONNECTION_TEST_4 = "4B314C545A497200C2FC6EB7"
        private const val CMD_CONNECTION_TEST_4_RESULT = "4B257C873DC2FC6EB701008876"
        private const val CMD_CONNECTION_TEST_5 = "4B304C543A417200DECDE495"
        private const val CMD_CONNECTION_TEST_5_RESULT = "4B802AED16DECDE49502008876"
        private const val CMD_CONNECTION_TEST_6 = "4B324C5467B97600A0A4ABCF"
        private const val CMD_CONNECTION_TEST_6_RESULT = "4BA0E65426A0A4ABCF02008876"
        private const val CMD_QUERY_MM_1 = "6D010000"
        private const val CMD_QUERY_MM_2 = "6D010100"
        private const val CMD_QUERY_MM_2_RESULT = "7D21AAEF"
        private const val CMD_RESTART = "4F0000000004"

        private const val STEP_CONNECT_TEST_1 = 1
        private const val STEP_CONNECT_TEST_2 = 2
        private const val STEP_FIRMWARE_CHECK_CODE = 3
        private const val STEP_FIRMWARE_FILE_SEND = 4
        private const val STEP_FIRMWARE_CRC_SEND = 5
        private const val STEP_CONNECT_TEST_3 = 6
        private const val STEP_CONNECT_TEST_4 = 7
        private const val STEP_IMAGE_CHECK_CODE = 8
        private const val STEP_CONNECT_TEST_5 = 9
        private const val STEP_CONNECT_TEST_6 = 10
        private const val STEP_IMAGE_QUERY_MM_1 = 11
        private const val STEP_IMAGE_QUERY_MM_2 = 12
        private const val STEP_IMAGE_FILE_SEND = 13
        private const val STEP_IMAGE_CRC_SEND = 14
        private const val STEP_RESTART = 15

        private val FIRMWARE_CHECK_CODES = longArrayOf(
            0xF7FF7F01, 0x00801F44, 0x8707770A, 0x8606760A,
            0x77099607, 0x01468607, 0xB6077709, 0xA6077709,
            0x16566405, 0xEFFB2A06, 0xB5077706, 0x000000CF,
            0x00801F60, 0x00801F64, 0x00801F68, 0x4012001B,
            0x40120019, 0x9F002430, 0x8F007F02, 0x00CF2030,
            0x00801F08, 0xBEBEBEBE
        )

        private val IMAGE_CHECK_CODES = longArrayOf(
            0x00800B70, 0x00800D06, 0x00800D08, 0x00800D04,
            0x00800D0A, 0x00800D04, 0x00800D04, 0x00800D0C,
            0x00800D0E, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x00800D04, 0x00800D04,
            0x00800D04, 0x00800D04, 0x008004C0, 0x0080029E,
            0x008002AA, 0x008002AA, 0x008002AA, 0x008002AA,
            0x0080023C, 0x0080024A, 0x008002AA, 0x00800296,
            0x00800274, 0x00000000, 0x00000000, 0x00000000,
            0x01430142, 0x1C220105, 0x12277607, 0x1C673C37,
            0x9417B307, 0x2002D117, 0x1C623C32, 0x9412B302,
            0x00CFD112, 0x00801E00, 0x007E2470, 0x01040142,
            0x12270166, 0x20173C27, 0x01441274, 0xAF177726,
            0xE80F2A0F, 0x950E7E25, 0x2A02D617, 0x7523E00A,
            0xA57F8F05, 0xAF6F3C85, 0x01651CF5, 0xE0010C65,
            0x771CD517, 0x0C61C617, 0x0516E004, 0x6006D617,
            0x0106F004, 0xD6176006, 0xB6176016, 0x12467719,
            0x1C673C36, 0x6007D117, 0xF0087614, 0xAF058506,
            0x2003BF03, 0x95062005, 0x01472007, 0xE7F60F17,
            0xA647770D, 0x1A251265, 0x2A052E15, 0x1245730D,
            0x1C353C35, 0x740BE806, 0x1322B405, 0xB6471F26,
            0x7409F005, 0x1322B405, 0xB2471E62, 0x2070006E,
            0x000000CF, 0x00801940, 0x008012E0, 0x00801920,
            0x00801E00, 0xFFFFFF88, 0xFFFFFFC8, 0x9F002430,
            0x12361225, 0x60020166, 0x64047303, 0x8F007F03,
            0x00CF2030, 0x00801FC0, 0x00800118, 0x007E2470,
            0xA647770C, 0xB6473006, 0x8707770B, 0x2FF6A607,
            0xE84D2A16, 0xE8542A26, 0xE0562A06, 0x6096A517,
            0xE8430C56, 0x3C257605, 0x86061C56, 0x000000C6,
            0x00801940, 0x00801920, 0x008000BC, 0x12236002,
            0x60367F26, 0xB6977726, 0xA637F03C, 0xE8072A26,
            0xE8082A36, 0xE0292A16, 0x61237222, 0x7221F01E,
            0xF01B6433, 0xA7277621, 0x1C763C27, 0x12728706,
            0xF013A307, 0xA5277E1A, 0x1C56A637, 0xA637B66E,
            0x1C76A727, 0xE81D2A06, 0x60027F19, 0x7F121223,
            0xB79E6017, 0x7216F016, 0x7F0F6013, 0x6006F012,
            0xD657770F, 0x60237214, 0x7F13F7F8, 0x7F13F00A,
            0x77132A02, 0x7613E003, 0xF007B607, 0xF7FC7613,
            0x76107F13, 0xF7F8770F, 0x76117F0C, 0x30578706,
            0x006E9706, 0x00CF2070, 0x008001DC, 0x00801940,
            0x00018128, 0x000180E4, 0x008012D0, 0x000183C0,
            0x00801946, 0x0080194A, 0x00018484, 0x00800B04,
            0x00801E00, 0xFFFFFF88, 0xFFFFFFC8, 0x00018414,
            0x50000094, 0x9F002430, 0xA7677733, 0xE0052A17,
            0xA7077732, 0x2A176002, 0x7730E859, 0x66A5A607,
            0xE8110F56, 0xE8510C56, 0x0C6566C5, 0xA117E84E,
            0xA207772C, 0x6004732C, 0x12257727, 0x3C923C35,
            0x1C571C25, 0xA527F032, 0xA6173C85, 0x3D04A437,
            0xA5471E56, 0x1E463D85, 0xA3571E56, 0xB3057521,
            0x1234751D, 0x3C933C34, 0x1C451C34, 0x60049605,
            0x6006B465, 0xA367D625, 0x6007761C, 0xC425F009,
            0x1C421252, 0xBF82AF06, 0xD4252004, 0x20062007,
            0xEFF50C37, 0xF01A6012, 0x1272C527, 0xAF031C52,
            0x2005BF82, 0x2004D527, 0x0C142003, 0x6012EFF5,
            0x0F7666C7, 0x770AE00B, 0x1267A607, 0x3C963C37,
            0x760A1C67, 0xB2271C67, 0x6002F001, 0x20308F00,
            0x000000CF, 0x008012EC, 0x008014FA, 0x008019C0,
            0x008016FD, 0x008019C2, 0x008019C7, 0x008012F0,
            0x007E2470, 0x86077723, 0x01462E86, 0x3E478707,
            0x2A072EF7, 0x2A06E81C, 0x751EE037, 0x3C541274,
            0xB6051C45, 0x13767E1D, 0x1E56A50E, 0x2A37B60E,
            0x7F1AE02B, 0xE8072A02, 0x771A761A, 0xA70EB607,
            0xB70E3037, 0x7718F021, 0xB6076016, 0x2A06F01D,
            0x7711E811, 0x2A36A697, 0x7614E009, 0xA5268606,
            0x95067614, 0xB6976046, 0xB6176016, 0x12236002,
            0xF00A7F11, 0xA6077708, 0x167663C7, 0x0F766347,
            0x7F0DE002, 0x7F0DF001, 0x2070006E, 0x000000CF,
            0x50000090, 0x00801E00, 0x00801940, 0x00800314,
            0xFFFFFF80, 0x00801E60, 0x008016FC, 0x00801920,
            0x50000098, 0x008001DC, 0x008001FC, 0x000185E0,
            0x007E2470, 0x8706761E, 0x2A072E17, 0xE8051267,
            0x34078706, 0x7F1A9706, 0x8606F02D, 0x2A062E46,
            0x8607E803, 0x96073426, 0x32768707, 0x2A071667,
            0x7F14E801, 0x870E7E12, 0x2A072E87, 0x7612E808,
            0x34178706, 0x7F119706, 0x3437870E, 0x770B970E,
            0x2F068607, 0xE8032A06, 0x34468607, 0x76079607,
            0x2E278707, 0xE8062A07, 0x34178706, 0x2C869706,
            0x96077708, 0x2070006E, 0x000000CF, 0x50000080,
            0x00018694, 0x00018670, 0x50000094, 0x00800400,
            0x50000088, 0x9F002430, 0x76071225, 0xB6077708,
            0xA6077708, 0xB6073036, 0x73066022, 0x63F66404,
            0x8F007F06, 0x00CF2030, 0xFFFFFF80, 0x00801E60,
            0x00801940, 0x00801980, 0x00800118, 0x007C24F0,
            0x014D122D, 0xA707773D, 0xE0722A27, 0x12DE7C3C,
            0x12D73C3E, 0x1CE73C97, 0x82071CC7, 0x16272D17,
            0xE0022A07, 0x7F363EB2, 0x3C9712D7, 0x1CC71CE7,
            0x2CB78E07, 0x2A0E167E, 0x7732E015, 0x72329E07,
            0x7F3212E3, 0x7F337F33, 0x7F336022, 0x7F3212E2,
            0x7F3112E2, 0x7F327232, 0x87067633, 0x97063127,
            0x7F313272, 0x12D77326, 0x12D63C37, 0x1C673C96,
            0x1C761236, 0x6006C526, 0x2C0E742B, 0xF00C1232,
            0x12639E14, 0x1C231C73, 0x9324A383, 0x37F38314,
            0x7324EFFD, 0x20068323, 0xEFF20C56, 0x8706761A,
            0x97061C57, 0x0C762CB6, 0x2C97E803, 0xE8200C57,
            0x8706761D, 0x97063527, 0x7F1D7F1D, 0x12D67710,
            0x3C9D3C36, 0x1C671CD6, 0x12ED8E07, 0x7F113EBD,
            0x7F116102, 0x7F102C82, 0x12E23F3E, 0x7F0E0142,
            0x014212D2, 0x7F117F0D, 0x72077F12, 0x7F0767C3,
            0x006C6002, 0x00CF20F0, 0x008012E8, 0x008012EC,
            0x008011C4, 0x008012E4, 0x000000A0, 0x00801120,
            0x008010E0, 0x00801064, 0x00800FB4, 0x000000B8,
            0x0080121C, 0x400C0000, 0x00800D68, 0x00801038,
            0x0080115C, 0x007825F0, 0x05106481, 0x77AD6006,
            0x7BAD9607, 0x79AE7DAE, 0x2A17A70B, 0xA60DE143,
            0x16676207, 0xE8DF2A07, 0x1C6575AB, 0x60270145,
            0xE80D0C57, 0x2A027FA9, 0x6007E935, 0x76A7B70B,
            0xB60777A8, 0xA60777A8, 0xB6073036, 0x66D7F12B,
            0xE0280F76, 0xB70B6007, 0x2A07A72D, 0x7FA2E004,
            0x7FA37FA3, 0xA72D7FA4, 0xE0042A17, 0x7FA27F9F,
            0x7FA07FA3, 0x7FA26642, 0x122E7FA3, 0x7FA37FA3,
            0x7FA361F2, 0x7FA272A4, 0x7FA16002, 0x67D77FA3,
            0xBE10B700, 0xB120011E, 0xB130012E, 0xB140013E,
            0x7F9E1202, 0x66E7A60D, 0xE0FC0F76, 0xB70B6007,
            0x769BA75D, 0x1E7E166E, 0x3C87A76D, 0x166E769A,
            0xA77D1E7E, 0x76983D07, 0x1E7E166E, 0x3D87A78D,
            0x16FE2D8F, 0x72951E7E, 0x73967F96, 0x72931623,
            0x7F8A7F96, 0x7F8A6132, 0x7F896002, 0x7F886002,
            0x7F876002, 0x7F917F89, 0x60327F85, 0x60027F85,
            0x60027F84, 0x60027F83, 0x12E77F82, 0x64063F47,
            0x97061C06, 0x16E72D47, 0x1C066446, 0x77889706,
            0x31068607, 0x86079607, 0x31F631E6, 0x960735E6,
            0x35068607, 0x72839607, 0x600E7F84, 0xF02112EA,
            0x72823348, 0x72827F81, 0x12E77F80, 0x172E3E87,
            0x3C2E014E, 0x1C6E7680, 0x177E8E0E, 0x2A082408,
            0x67E7E7F0, 0x6307B700, 0x12A7B710, 0xB7203D47,
            0xB1300117, 0xB1400127, 0xB7503F87, 0x7F671202,
            0x6407200A, 0x87071C07, 0xE7DA0F7A, 0x7871600A,
            0x726EF00D, 0x726E7F6D, 0x12E77F6C, 0x172E3E87,
            0x3C2E014E, 0x8E0E1C8E, 0x200A177E, 0x1C066446,
            0x0F6A8606, 0x7762E7EE, 0x31068607, 0x86079607,
            0x31F631E6, 0x86079607, 0x96073506, 0x67E77F53,
            0x6317B700, 0xBE20B710, 0xB130011E, 0xB140012E,
            0xB150013E, 0xB70BF05D, 0x0F5664B5, 0x842DE044,
            0xB68D6186, 0xB69D6206, 0xB6AD7658, 0xB6BD6766,
            0xB6CD6546, 0xB6DD64C6, 0xB6ED6476, 0xB6FD6446,
            0x73501275, 0x3E821272, 0x1CD61256, 0x2C8FA606,
            0x177616F7, 0x1C363C26, 0x17278706, 0x0A7C2AB5,
            0x2B052005, 0x0FC4E7EF, 0xA51DE005, 0x1C657648,
            0xB5067648, 0xB60064B6, 0x0117B710, 0x0127B120,
            0x3F87B130, 0xBC50B740, 0xB160011C, 0xB170012C,
            0xB180013C, 0xBE907E40, 0xB7A06007, 0xB7B0773F,
            0xB7C06767, 0x64F5F015, 0xE0100F56, 0x9746763C,
            0x30078709, 0x87099709, 0x97093017, 0x87767639,
            0x97763077, 0x8707871D, 0xF00400D7, 0xB7006447,
            0x7F1E1202, 0xA76A7A34, 0xE0042A17, 0x7F326002,
            0xB76A6007, 0xA20A7A32, 0xE6AE2A12, 0x60077F2F,
            0xF6AAB70A, 0x008012E4, 0x008016FC, 0x008019C0,
            0x50000094, 0xFFFFFF96, 0x00800314, 0xFFFFFF80,
            0x00801E60, 0x00801940, 0x00800F0C, 0x00800F34,
            0x00800FE4, 0x00800EE8, 0x00800F5C, 0x00801010,
            0x00800DAC, 0x00801178, 0x008010E0, 0x00801064,
            0x00800FB4, 0x000000A0, 0x00801038, 0x00800554,
            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0x000000B0,
            0x008010B0, 0x000000F0, 0x00801120, 0x0080115C,
            0x400C0000, 0x000000B8, 0x00800D84, 0x000080FF,
            0x0000C0FF, 0x00801A00, 0xFFFFFF88, 0xFFFFFFD0,
            0x008012E8, 0x76880002, 0x00000088, 0xE0000000,
            0x50000000, 0x008012EC, 0x0080058C, 0x008014FA,
            0x6006750B, 0x3283720B, 0x6011F00D, 0x3E170E17,
            0x1727E801, 0x2A042404, 0x9705E7F8, 0x20352006,
            0xE8030F36, 0x60841267, 0x00CFF7F0, 0x00801A00,
            0xEDB88320, 0x007D24B0, 0x600E7D13, 0xBE077713,
            0x7712BE7D, 0x7712BE07, 0x7712BE07, 0x7712BE67,
            0x7F12BE07, 0x77137413, 0xBE9D9407, 0x73126002,
            0x7F126405, 0x73126012, 0x64057413, 0x7F127F10,
            0x76137713, 0xBE479607, 0x6086BE57, 0x006DB667,
            0x00CF20B0, 0x00801940, 0x00801951, 0x008012E8,
            0x008016FC, 0x008012EC, 0x008014FA, 0x00800A40,
            0x00801F80, 0x00801920, 0x000000C8, 0x008000F0,
            0x00000088, 0x00801FC0, 0x000181A8, 0x00801900,
            0x00C20100, 0x9F002430, 0x87077712, 0x6216A717,
            0xE80F0F67, 0xE8060C76, 0xE8082AA7, 0x0F676206,
            0xF00BE012, 0x0F676226, 0xF00BE00E, 0x6013720A,
            0x7209F002, 0x7F096073, 0x7609F006, 0x6012B706,
            0x7607F003, 0x6002B706, 0x20308F00, 0x000000CF,
            0x00801920, 0x00801947, 0x00801900, 0x008001DC,
            0x00801951, 0xBEBEBEBE, 0xBEBEBEBE, 0xBEBEBEBE,
            0x12107116, 0x18117116, 0x77151012, 0x3006C607,
            0x7714D607, 0x3006C607, 0x7713D607, 0x75147614,
            0x94077414, 0x20F720F5, 0xE7FB0F56, 0x77116016,
            0x60019607, 0x60036002, 0x60056004, 0x60076006,
            0x60096008, 0x600B600A, 0x600D600C, 0x600F600E,
            0x7F0A7F0A, 0x0000F7FF, 0x008018FC, 0x00800000,
            0x40130002, 0x40140002, 0xFFF81000, 0x00000400,
            0x00000000, 0xFFF80000, 0x00800D10, 0x00800BF0,
            0x007E2470, 0x66427F2A, 0x7E2A7F2A, 0x3037870E,
            0x61E2970E, 0x870E7F27, 0x970E3437, 0x7F246642,
            0x7F256052, 0x7F267226, 0x7F236072, 0x7F247224,
            0x7F216092, 0x7F227222, 0x7F1F6062, 0x7F206092,
            0x7F1D6082, 0x7F1E6782, 0x7F1B60A2, 0x7F1C65A2,
            0x7F196002, 0x7F1B6012, 0x7F193272, 0x7F146012,
            0x6293721A, 0x721A7F1A, 0x7F186033, 0x731A721A,
            0x721A7F17, 0x7F156033, 0x6013721A, 0x7F197F14,
            0x7F0B6012, 0x7F197F19, 0x60127F1A, 0x7F1A7F1A,
            0x2D877F1B, 0xE0040F72, 0x7F1A7F1A, 0x7F167F1B,
            0x7F1B7F1B, 0x2070006E, 0x000000CF, 0x00018930,
            0x00800DFC, 0x400E006C, 0x0080121C, 0x00000088,
            0x00801248, 0x00800DAC, 0x000000E0, 0x00800EB0,
            0x000000E1, 0x000000E2, 0x000000CE, 0x000000E3,
            0x000000E4, 0x00800ED0, 0x00800F88, 0x00800F34,
            0x00800FE4, 0x0080108C, 0x00800EE8, 0x00801178,
            0x00800F0C, 0x00800F5C, 0x00801010, 0x00800A74,
            0x008006C4, 0x00CF00CF, 0x00CF00CF, 0x00CF00CF,
            0x75117411, 0x0F546007, 0xF00CE009, 0x1C731243,
            0x1C721252, 0xB203A202, 0xF0022007, 0x0546760C,
            0xE7F40D67, 0x760B750B, 0x0F656007, 0xF009E006,
            0x1C731263, 0x2007B403, 0x0565F002, 0x0D571274,
            0x00CFE7F7, 0x008012D0, 0x008012D0, 0x008012E0,
            0x00801706, 0x008012E0, 0x77050102, 0x96172C06,
            0x86179127, 0xEFFD37F6, 0x82277702, 0x00CF0142,
            0x400C0000, 0x77080162, 0x31268607, 0x2C069607,
            0x92279617, 0x85177605, 0xEFFC37F5, 0x87068226,
            0x97063527, 0x00CF0162, 0x400C0000, 0x3C361226,
            0x1C263C62, 0x770F2406, 0x16462D84, 0x740E8517,
            0x1E651645, 0x86079517, 0x96073016, 0x34268607,
            0x86079607, 0x96073406, 0x1200F001, 0x85077606,
            0x16453304, 0xEFF92A05, 0x30078706, 0x60079706,
            0x00CF9726, 0xE0001000, 0xFF000000, 0x12266641,
            0x12652C16, 0x1C651C65, 0x3C571257, 0x1C671C57,
            0x01420572, 0xE81F2A06, 0x85177722, 0x16357322,
            0x1E457422, 0x85079517, 0x95073015, 0x34258507,
            0x85079507, 0x95073405, 0x1200F001, 0x84077519,
            0x16343303, 0xEFF92A04, 0x2A062406, 0x8405E7F7,
            0x94053004, 0x2A029625, 0x7614E821, 0x24060326,
            0x2D847710, 0x85171646, 0x1635730F, 0x95171E65,
            0x30168607, 0x86079607, 0x96073426, 0x34068607,
            0xF0019607, 0x76061200, 0x33048507, 0x2A051645,
            0x8706EFF9, 0x97063007, 0x97266007, 0x000000CF,
            0xE0001000, 0xFF000000, 0x006DDCFF, 0x00011940,
            0x007E2470, 0x014E123E, 0x7F030142, 0x7F0312E2,
            0x2070006E, 0x000000CF, 0x0080121C, 0x00801248,
            0x9F002430, 0x2E427F04, 0xEFFC2A02, 0x20308F00,
            0x000000CF, 0x00801274, 0x9F002430, 0x7F046012,
            0x01427F05, 0x7F043412, 0x20308F00, 0x000000CF,
            0x0080121C, 0x0080129C, 0x00801248, 0x9F002430,
            0x7F046012, 0x77057F05, 0x7F051672, 0x20308F00,
            0x000000CF, 0x0080121C, 0x0080129C, 0x000000FD,
            0x00801248, 0x9F002430, 0x7F057205, 0x67F77F06,
            0x7F051672, 0x20308F00, 0x000000CF, 0x000000B7,
            0x0080121C, 0x0080129C, 0x00801248, 0x9F002430,
            0x7F057205, 0x77067F06, 0x01421E72, 0x8F007F06,
            0x00CF2030, 0x000000B7, 0x0080121C, 0x0080129C,
            0xFFFFFF80, 0x00801248, 0x9F002430, 0x7F057205,
            0x77067F06, 0x34121672, 0x8F007F06, 0x00CF2030,
            0x000000B7, 0x0080121C, 0x0080129C, 0x000000FC,
            0x00801248, 0x007D24B0, 0x014E122E, 0x12D27D06,
            0x12E27F06, 0x12D27F06, 0x7F057F04, 0x006D0142,
            0x00CF20B0, 0x000000B8, 0x0080121C, 0x00801248,
            0x0080129C, 0x9F002430, 0x7F057205, 0x77067F06,
            0x7F061672, 0x20308F00, 0x000000CF, 0x000000B9,
            0x0080121C, 0x0080129C, 0x000000DF, 0x00801248,
            0x9F002430, 0x7F057205, 0x01427F06, 0x7F053452,
            0x20308F00, 0x000000CF, 0x000000B9, 0x0080121C,
            0x0080129C, 0x00801248, 0x9F002430, 0x7F057205,
            0x77067F06, 0x7F061672, 0x20308F00, 0x000000CF,
            0x000000B9, 0x0080121C, 0x0080129C, 0x000000EF,
            0x00801248, 0x9F002430, 0x7F057205, 0x01427F06,
            0x7F053442, 0x20308F00, 0x000000CF, 0x000000B9,
            0x0080121C, 0x0080129C, 0x00801248, 0x007E2470,
            0x014E122E, 0x7F047204, 0x7F0412E2, 0x2070006E,
            0x000000CF, 0x000000BB, 0x0080121C, 0x00801248,
            0x007E2470, 0x014E122E, 0x60F27F07, 0x12E27F07,
            0x2C827F06, 0x122E7F05, 0x12E27F05, 0x2070006E,
            0x000000CF, 0x00801064, 0x00800FB4, 0x00801038,
            0x9F002430, 0x60627F04, 0x7F047F04, 0x20308F00,
            0x000000CF, 0x00801064, 0x00800FB4, 0x00801038,
            0x9F002430, 0x60427F04, 0x7F047F04, 0x20308F00,
            0x000000CF, 0x00801064, 0x00800FB4, 0x00801038,
            0x007D24B0, 0x014E122E, 0x014D123D, 0x7F077F07,
            0x7F0761F2, 0x7F0612E2, 0x7F0512D2, 0x7F067F06,
            0x20B0006D, 0x000000CF, 0x008010E0, 0x00801064,
            0x00800FB4, 0x00801038, 0x00801100, 0x9F002430,
            0x7F047204, 0x2A022E12, 0x8F00E7FB, 0x00CF2030,
            0x000000C0, 0x008010B0, 0x007E2470, 0x720D7F0D,
            0x2C827F0E, 0x2C827F0D, 0x122E7F0C, 0x2D073C8E,
            0x2C82167E, 0x1E2E7F09, 0x77083C8E, 0x2C82167E,
            0x1E2E7F06, 0x12E27F07, 0x2070006E, 0x000000CF,
            0x00801064, 0x0000009F, 0x00800FB4, 0x00FFFF00,
            0x00801038, 0x007D24B0, 0x7D0C122E, 0x600312D2,
            0x7F0C7F0C, 0x7F0D7F0D, 0x7F0E720E, 0x7F0D2C82,
            0x1212011E, 0x12E27F0C, 0x7F0A0142, 0x7F067F0B,
            0x67C312D2, 0x006D7F03, 0x00CF20B0, 0x000000A0,
            0x00801120, 0x008010E0, 0x0080115C, 0x00801064,
            0x000000D8, 0x00800FB4, 0x00801038, 0x007D24B0,
            0x014E122E, 0x870D7D07, 0x970D3127, 0x7F056002,
            0x7F0412E2, 0x3527870D, 0x006D970D, 0x00CF20B0,
            0x400C0000, 0x00800D68, 0x007D24B0, 0x014E122E,
            0x870D7D07, 0x970D3127, 0x7F053272, 0x7F0412E2,
            0x3527870D, 0x006D970D, 0x00CF20B0, 0x400C0000,
            0x00800D68, 0x007E2470, 0x870E7E07, 0x970E3127,
            0x7F056402, 0x7F042C82, 0x3527870E, 0x006E970E,
            0x00CF2070, 0x400C0000, 0x00800D68, 0x007E2470,
            0x870E7E08, 0x970E3127, 0x7F077207, 0x7F062C82,
            0x3527870E, 0x0162970E, 0x2070006E, 0x000000CF,
            0x400C0000, 0x000000C0, 0x00800D68, 0xBEBEBEBE,
            0x00018178, 0x00018164, 0x00018150, 0x0001813C
        )
    }
}